#pragma once
#include<iostream>
#include<tuple>
#include"SkipList.h"
#include"ResourceManage.h"
#include"IO.h"
#include"IndexRecord.h"
using namespace std;


//文件格式
//文件开头三个数据 第一个是版本号 第二个是 有效数据长度 第三个是 LSMtree 有序列表数量（用于LSMtree合并）
//每个列表由长度描述数据和具体数据组成
class SkipListIndex : public ResourceManage{
public:

    SkipList<Basic*, LocInfo*>* skipList;      // key允许重复

    SkipList<Basic*, LocInfo*>* snapShot;

    vector<IndexRecord*> records;

    int32_t keySize;

    int32_t locInfoSize;

    static int32_t compactThreshold;

    int getTurpleSize() const {
        return keySize + locInfoSize;
    }

    char* getDataBuffer() const {
        int32_t dataSize = skipList->getSize();
        if(dataSize == 0){ return nullptr; }
        int64_t bufferLen = dataSize * IndexRecord::getIndexRecordSize(resourceInfo.getDataType());
        char* buffer = new char[bufferLen];
        int64_t ind = 0;
        IndexOpType indAddType = INDADD;
        for(Node<Basic*, LocInfo*>* node = skipList->getDataStartNode(); node->nodeType != LEVELEND; node = node->nextNode){
            memcpy(&buffer[ind], node->key->getByteData(), keySize);
            ind += keySize;
            memcpy(&buffer[ind], node->val, locInfoSize);
            ind += locInfoSize;
            memcpy(&buffer[ind], &indAddType, sizeof (IndexOpType));
            ind += sizeof (IndexOpType);
        }
        return buffer;
    }

    void initData(const vector<IndexRecord*>& optimizMergeVec){
        int32_t slLevelMultiple = skipList->getLevelMultiple();
        MemUtil::clearPtrMem(skipList);     //里面没有数据 就同步释放了（异步释放有一定开销）
        MemUtil::clearPtrMem(snapShot);     //里面没有数据 就同步释放了（异步释放有一定开销）
        vector<Node<Basic*, LocInfo*>*> skipListNodeVec, snapShotNodeVec;
        for(IndexRecord* indRec : optimizMergeVec){
            if(indRec->indexOpType == INDDEL){
                throw string("file error");
            }
            //在这里不拷贝 直接把数据节点的指针指向record的数据
            skipListNodeVec.push_back(new Node<Basic*, LocInfo*>(
                indRec->opVal,
                indRec->locInfo,
                0, nullptr, nullptr, nullptr, nullptr));
            //在这里需要拷贝 因为record的数据已经给出去了
            snapShotNodeVec.push_back(new Node<Basic*, LocInfo*>(
                BasicUtil::getCopyBasicData(indRec->opVal),
                new LocInfo(indRec->locInfo),
                0, nullptr, nullptr, nullptr, nullptr));
            //把record的指针指向空 防止之后被释放
            indRec->opVal = nullptr;
            indRec->locInfo = nullptr;
        }
        skipList = new SkipList<Basic*, LocInfo*>(skipListNodeVec, slLevelMultiple);
        snapShot = new SkipList<Basic*, LocInfo*>(snapShotNodeVec, slLevelMultiple);
    }

    void rollBackAccordRecord(IndexRecord* rec){
        const IndexOpType& opType = rec->indexOpType;
        Basic* opVal = rec->opVal;
        LocInfo* locInfo = rec->locInfo;
        if(opType == INDADD){
            skipList->erase(opVal, locInfo);
        }
        else if(opType == INDDEL){  //都是操作成功了才有的记录 所以一定会成功
            skipList->insert(BasicUtil::getCopyBasicData(opVal), new LocInfo(locInfo));
        }
        else{
            throw string("indOpType error");
        }
    }


    virtual void readFromDisk(){
        fstream f;
        tuple<int64_t, int64_t, int64_t> metaInfo;
        try {
            f = getCurrentSLIndexFile(resourceInfo, metaInfo);
        }  catch (string) { return; }
        const TYPE& dataType = resourceInfo.getDataType();
        //获取没有合并的有序列表集合
        const vector<vector<IndexRecord*>>& unMergeVec = IndexRecordUtil::mutexReadRecVec(f, std::get<1>(metaInfo), dataType);
        //获取归并排序后的总体有序列表
        vector<IndexRecord*> mergeVec = IndexRecordUtil::mergeRecVec(unMergeVec);
        //将列表优化
        vector<IndexRecord*> optimizMergeVec = IndexRecordUtil::getOptimizIndexRecordVec(mergeVec, true);
        //当有序列表数量超过阈值 全量合并 写到另一个文件中
        if(std::get<2>(metaInfo) >= compactThreshold){
            //版本号+1
            writeToSpareSLIndexFile(optimizMergeVec, resourceInfo, std::get<0>(metaInfo) + 1);
        }
        initData(optimizMergeVec);
        MemUtil::asyncClearVecMem(optimizMergeVec);
    }



    virtual void writeToDisk(){
        //不支持
    }


    static vector<LocInfo*> getFloorOrCeiling(SkipList<Basic*, LocInfo*>* skipList, Basic* ele, const bool& isFloor){
        vector<LocInfo*> resVec;
        Node<Basic*, LocInfo*>* node = isFloor ? skipList->getLastFloorNode(ele) : skipList->getFirstCeilingNode(ele);
        for(; node->isNormalNode(); node = isFloor ? node->lastNode : node->nextNode){
            resVec.push_back(node->val);
        }
        return resVec;
    }

    static vector<LocInfo*> getLowerOrHigher(SkipList<Basic*, LocInfo*>* skipList, Basic* ele, const bool& isLower){
        vector<LocInfo*> resVec;
        Node<Basic*, LocInfo*>* node = isLower ? skipList->getFirstCeilingNode(ele)->lastNode : skipList->getLastFloorNode(ele)->nextNode;
        for(; node->isNormalNode(); node = isLower ? node->lastNode : node->nextNode){
            resVec.push_back(node->val);
        }
        return resVec;
    }

    //创建切换的两个索引文件，并返回数据版本大的文件的描述符。
    static fstream createSwitchedSLIndexFiles(const ResourceInfo& resInfo){
        const pair<string, string>& switchFullPaths = resInfo.getSwitchedFullPaths();
        IOUtil::createSLIndexFile(switchFullPaths.first, 0);
        return IOUtil::createSLIndexFile(switchFullPaths.second, 1);
    }

    //获取指定的索引文件的文件描述符 再将元数据引用传进去赋值
    static fstream getSpecificSLIndexFile(const ResourceInfo& resInfo, tuple<int64_t, int64_t, int64_t>& metaInfo, const bool& needCurrent){
        const pair<string, string>& switchFullPaths = resInfo.getSwitchedFullPaths();
        fstream f0 = IOUtil::getGoodFstream(switchFullPaths.first, ios::in | ios::out | ios::binary),
                f1 = IOUtil::getGoodFstream(switchFullPaths.second, ios::in | ios::out | ios::binary);
        const tuple<int64_t, int64_t, int64_t>& metaInfo0 = IOUtil::readTupleMetaInfo(f0);
        const tuple<int64_t, int64_t, int64_t>& metaInfo1 = IOUtil::readTupleMetaInfo(f1);
        const bool& isZeroCurrent = std::get<0>(metaInfo0) > std::get<0>(metaInfo1);
        if(isZeroCurrent ^ needCurrent){
            metaInfo = metaInfo1;
            return f1;
        }
        metaInfo = metaInfo0;
        return f0;
    }

    //获取当前正在使用的索引文件的文件描述符 再将元数据引用传进去赋值
    static fstream getCurrentSLIndexFile(const ResourceInfo& resInfo, tuple<int64_t, int64_t, int64_t>& metaInfo){
        return getSpecificSLIndexFile(resInfo, metaInfo, true);
    }

    //获取当前没在使用的的索引文件的文件描述符
    static fstream getSpareSLIndexFile(const ResourceInfo& resInfo){
        tuple<int64_t, int64_t, int64_t> metaInfo;
        return getSpecificSLIndexFile(resInfo, metaInfo, false);
    }

    //将压缩后的数据写入空闲文件，并原子性的把空闲文件置为当前文件
    static void writeToSpareSLIndexFile(const vector<IndexRecord*>& optimizMergeVec, const ResourceInfo& resInfo, const int64_t& dataVersion){
        fstream f = getSpareSLIndexFile(resInfo);   //获取空闲的文件的文件描述符
        char* byteData = IndexRecordUtil::getIndRecVecByteData(optimizMergeVec, resInfo.getDataType());
        //通过版本号来做到原子性的当前文件替换
        IOUtil::writeSLIndexFileAndSwitch(f, byteData, optimizMergeVec.size() * IndexRecord::getIndexRecordSize(resInfo.getDataType()), dataVersion);
        MemUtil::asyncClearPtrArrMem(byteData);
    }

public:

    SkipListIndex(const ResourceInfo& resInfo, const int32_t& slLevelMultiple = 2):
        ResourceManage(resInfo)
    {
        if(resInfo.getDiskType() != SLINDEX){
            throw string("diskType error");
        }
        this->keySize = BasicUtil::getDataSize(resourceInfo.getDataType());
        this->locInfoSize = LocInfo::getLocInfoSize();
        this->skipList = new SkipList<Basic*, LocInfo*>(slLevelMultiple);
        this->snapShot = new SkipList<Basic*, LocInfo*>(slLevelMultiple);
    }

    //析构函数里面都是同步释放 仅在最头部控制异步
    virtual ~SkipListIndex(){
        MemUtil::clearPtrMem(skipList);
        MemUtil::clearPtrMem(snapShot);
    }


    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> get(Basic* ele) const {
        return skipList->find(ele);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> snapShotGet(Basic* ele) const {
        return snapShot->find(ele);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> getFloor(Basic* ele) const {
        return getFloorOrCeiling(skipList, ele, true);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> snapShotGetFloor(Basic* ele) const {
        return getFloorOrCeiling(snapShot, ele, true);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> getCeiling(Basic* ele) const {
        return getFloorOrCeiling(skipList, ele, false);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> snapShotGetCeiling(Basic* ele) const {
        return getFloorOrCeiling(snapShot, ele, false);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> getLower(Basic* ele) const {
        return getLowerOrHigher(skipList, ele, true);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> snapShotGetLower(Basic* ele) const {
        return getLowerOrHigher(snapShot, ele, true);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> getHigher(Basic* ele) const {
        return getLowerOrHigher(skipList, ele, false);
    }

    //检索得到的是未经拷贝的原数据
    vector<LocInfo*> snapShotGetHigher(Basic* ele) const {
        return getLowerOrHigher(snapShot, ele, false);
    }

    //如果原本有key val都符合的就不插入
    void put(Basic* ele, LocInfo* locInfo, const bool& needRecord = true){
        //添加成功且需要记录才记录
        if(skipList->insert(ele, locInfo)){
            if(needRecord){                                            //ele locInfo 已经作为节点数据了 所以要拷贝
                records.push_back(new IndexRecord(BasicUtil::getCopyBasicData(ele), new LocInfo(locInfo), INDADD)); // 添加记录
            }
        }
        else{//没有成功插入 需要释放
            MemUtil::clearPtrMem(ele);
            MemUtil::clearPtrMem(locInfo);
        }
    }

    //删除所有key为ele val为locInfo的。但是只记录一次。
    void remove(Basic* ele, LocInfo* locInfo, const bool& needRecord = true){
        //删除成功且需要记录才记录
        if(skipList->erase(ele, locInfo) && needRecord){
            records.push_back(new IndexRecord(ele, locInfo, INDDEL)); // 删除记录
        }
        else{
            //没用在记录里 就需要释放
            MemUtil::clearPtrMem(ele);
            MemUtil::clearPtrMem(locInfo);
        }
    }


    //获取提交记录并清空资源的记录
    virtual vector<IndexRecord*> getComitIndexRecords(){
        makeSnapShot();
        vector<IndexRecord*> optimizVec = IndexRecordUtil::getOptimizIndexRecordVec(records, false);
        records.clear();
        return optimizVec;
    }


    virtual void memRollBack(){
        for(int i = records.size() - 1; i >= 0; i--){
            rollBackAccordRecord(records[i]);
        }
        MemUtil::asyncClearVecMem(records);
        //列表需要同步清空 因为可能马上就会被使用 记录可以异步释放
        records.clear();
    };

    virtual void makeSnapShot(){
        getSnapShotLock(WRITE);
        for(IndexRecord* indRec : records){
            const IndexOpType& indOpType = indRec->indexOpType;
            if(indOpType == INDADD){   //都是操作成功了才有的记录 所以一定会成功
                snapShot->insert(BasicUtil::getCopyBasicData(indRec->opVal), new LocInfo(indRec->locInfo));
            }
            else if(indOpType == INDDEL){
                snapShot->erase(indRec->opVal, indRec->locInfo);
            }
            else{
                throw string("indOpType error");
            }
        }
        releaseSnapShotLock();
    }

    static void setCompactThreshold(const int32_t& threshold){
        compactThreshold = threshold;
    }


    //传入的记录在函数里释放
    static void increWriteToDisk(const ResourceInfo& resInfo, const vector<IndexRecord*>& records, const bool& checkIsBeenWriten = false){
        if(records.empty()){ return; }
        const TYPE& dataType = records.front()->opVal->getType();
        fstream f;
        tuple<int64_t, int64_t, int64_t> metaInfo;
        try{
            f = getCurrentSLIndexFile(resInfo, metaInfo);
        }  catch(string){
            f = createSwitchedSLIndexFiles(resInfo);
            metaInfo = make_tuple(1, sizeof (int64_t) * 3, 0);
        }
        //获取bytedata
        const char* byteData = IndexRecordUtil::getIndRecVecByteData(records, dataType);
        //当不检查 或者数据没写过的话才会写入
        if(!checkIsBeenWriten || !IOUtil::isIndexRecordBeenWritten(f, std::get<1>(metaInfo), byteData, records.size() * IndexRecord::getIndexRecordSize(dataType))){
            //增量写入
            IOUtil::appIndexRecordFromEnd(f, std::get<1>(metaInfo), std::get<2>(metaInfo), byteData, records.size() * IndexRecord::getIndexRecordSize(dataType));
        }
        //释放
        MemUtil::asyncClearPtrArrMem(byteData);
        MemUtil::asyncClearVecMem(records);
        f.close();
    }
};




